using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using OpenTK.Core;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Common.Input;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace OpenTK.Windowing.Desktop
{
	public class NativeWindow : IDisposable
	{
		private Vector2i _cachedWindowClientSize;

		private Vector2i _cachedWindowLocation;

		private Vector2 _lastReportedMousePos;

		private static ConcurrentQueue<ExceptionDispatchInfo> _callbackExceptions = new ConcurrentQueue<ExceptionDispatchInfo>();

		private unsafe Cursor* _glfwCursor;

		private MouseCursor _managedCursor = MouseCursor.Default;

		private readonly JoystickState[] _joystickStates = new JoystickState[16];

		private VSyncMode _vSync;

		private WindowIcon _icon;

		private string _title;

		private MonitorHandle _currentMonitor;

		private bool _isFocused;

		private bool _isVisible;

		private WindowState _windowState;

		private WindowBorder _windowBorder;

		private Vector2i _location;

		private Vector2i _size;

		private Vector2i? _minimumSize;

		private Vector2i? _maximumSize;

		private (int numerator, int denominator)? _aspectRatio;

		private GLFWCallbacks.WindowPosCallback _windowPosCallback;

		private GLFWCallbacks.WindowSizeCallback _windowSizeCallback;

		private GLFWCallbacks.WindowIconifyCallback _windowIconifyCallback;

		private GLFWCallbacks.WindowMaximizeCallback _windowMaximizeCallback;

		private GLFWCallbacks.WindowFocusCallback _windowFocusCallback;

		private GLFWCallbacks.CharCallback _charCallback;

		private GLFWCallbacks.ScrollCallback _scrollCallback;

		private GLFWCallbacks.WindowRefreshCallback _windowRefreshCallback;

		private GLFWCallbacks.WindowCloseCallback _windowCloseCallback;

		private GLFWCallbacks.KeyCallback _keyCallback;

		private GLFWCallbacks.CursorEnterCallback _cursorEnterCallback;

		private GLFWCallbacks.MouseButtonCallback _mouseButtonCallback;

		private GLFWCallbacks.CursorPosCallback _cursorPosCallback;

		private GLFWCallbacks.DropCallback _dropCallback;

		private GLFWCallbacks.JoystickCallback _joystickCallback;

		[Obsolete("Use the Monitors.OnMonitorConnected event instead.", true)]
		private GLFWCallbacks.MonitorCallback _monitorCallback;

		private static List<ExceptionDispatchInfo> _localThreadExceptions = new List<ExceptionDispatchInfo>();

		private bool _disposedValue;

		public unsafe Window* WindowPtr { get; protected set; }

		public KeyboardState KeyboardState { get; } = new KeyboardState();


		[Obsolete("Use WasKeyDown instead.", true)]
		public KeyboardState LastKeyboardState => null;

		public IReadOnlyList<JoystickState> JoystickStates => _joystickStates;

		[Obsolete("Use WasButtonDown, GetAxisPrevious and GetHatPrevious instead.", true)]
		public IReadOnlyList<JoystickState> LastJoystickStates => null;

		public unsafe Vector2 MousePosition
		{
			get
			{
				return _lastReportedMousePos;
			}
			set
			{
				GLFW.SetCursorPos(WindowPtr, value.X, value.Y);
			}
		}

		[Obsolete("Use Delta member of the MouseState property instead.", true)]
		public Vector2 MouseDelta => Vector2.Zero;

		public MouseState MouseState { get; } = new MouseState();


		[Obsolete("Use WasButtonDown and PreviousPosition members of the MouseState property instead.", true)]
		public MouseState LastMouseState => null;

		public bool IsAnyKeyDown => KeyboardState.IsAnyKeyDown;

		public bool IsAnyMouseButtonDown => MouseState.IsAnyButtonDown;

		public VSyncMode VSync
		{
			get
			{
				if (Context == null)
				{
					throw new InvalidOperationException("Cannot control vsync when running with ContextAPI.NoAPI.");
				}
				return _vSync;
			}
			set
			{
				if (Context == null)
				{
					throw new InvalidOperationException("Cannot control vsync when running with ContextAPI.NoAPI.");
				}
				switch (value)
				{
				case VSyncMode.On:
					Context.SwapInterval = 1;
					break;
				case VSyncMode.Off:
					Context.SwapInterval = 0;
					break;
				}
				_vSync = value;
			}
		}

		public unsafe WindowIcon Icon
		{
			get
			{
				return _icon;
			}
			set
			{
				OpenTK.Windowing.Common.Input.Image[] images = value.Images;
				Span<GCHandle> span = stackalloc GCHandle[images.Length];
				Span<OpenTK.Windowing.GraphicsLibraryFramework.Image> span2 = stackalloc OpenTK.Windowing.GraphicsLibraryFramework.Image[images.Length];
				for (int i = 0; i < images.Length; i++)
				{
					OpenTK.Windowing.Common.Input.Image image = images[i];
					span[i] = GCHandle.Alloc(image.Data, GCHandleType.Pinned);
					byte* pixels = (byte*)(void*)span[i].AddrOfPinnedObject();
					span2[i] = new OpenTK.Windowing.GraphicsLibraryFramework.Image(image.Width, image.Height, pixels);
				}
				GLFW.SetWindowIcon(WindowPtr, span2);
				Span<GCHandle> span3 = span;
				for (int j = 0; j < span3.Length; j++)
				{
					GCHandle gCHandle = span3[j];
					gCHandle.Free();
				}
				_icon = value;
			}
		}

		public bool IsEventDriven { get; set; }

		public unsafe string ClipboardString
		{
			get
			{
				return GLFW.GetClipboardString(WindowPtr);
			}
			set
			{
				GLFW.SetClipboardString(WindowPtr, value);
			}
		}

		public unsafe string Title
		{
			get
			{
				return _title;
			}
			set
			{
				GLFW.SetWindowTitle(WindowPtr, value);
				_title = value;
			}
		}

		public ContextAPI API { get; }

		public ContextProfile Profile { get; }

		public ContextFlags Flags { get; }

		public Version APIVersion { get; }

		public IGLFWGraphicsContext Context { get; }

		public unsafe MonitorHandle CurrentMonitor
		{
			get
			{
				return _currentMonitor;
			}
			set
			{
				Monitor* monitor = value.ToUnsafePtr<Monitor>();
				VideoMode* videoMode = GLFW.GetVideoMode(monitor);
				GLFW.SetWindowMonitor(WindowPtr, monitor, _location.X, _location.Y, _size.X, _size.Y, videoMode->RefreshRate);
				_currentMonitor = value;
			}
		}

		public bool IsFocused => _isFocused;

		public unsafe bool IsVisible
		{
			get
			{
				return _isVisible;
			}
			set
			{
				if (value != _isVisible)
				{
					_isVisible = value;
					if (_isVisible)
					{
						GLFW.ShowWindow(WindowPtr);
					}
					else
					{
						GLFW.HideWindow(WindowPtr);
					}
				}
			}
		}

		public bool Exists { get; private set; }

		public unsafe bool IsExiting => GLFW.WindowShouldClose(WindowPtr);

		public unsafe WindowState WindowState
		{
			get
			{
				return _windowState;
			}
			set
			{
				if (_windowState == WindowState.Fullscreen && value != WindowState.Fullscreen)
				{
					GLFW.SetWindowMonitor(WindowPtr, null, _cachedWindowLocation.X, _cachedWindowLocation.Y, _cachedWindowClientSize.X, _cachedWindowClientSize.Y, 0);
				}
				switch (value)
				{
				case WindowState.Normal:
					GLFW.RestoreWindow(WindowPtr);
					break;
				case WindowState.Minimized:
					GLFW.IconifyWindow(WindowPtr);
					break;
				case WindowState.Maximized:
					GLFW.MaximizeWindow(WindowPtr);
					break;
				case WindowState.Fullscreen:
				{
					_cachedWindowClientSize = ClientSize;
					_cachedWindowLocation = Location;
					Monitor* monitor = CurrentMonitor.ToUnsafePtr<Monitor>();
					VideoMode* videoMode = GLFW.GetVideoMode(monitor);
					GLFW.SetWindowMonitor(WindowPtr, monitor, 0, 0, videoMode->Width, videoMode->Height, videoMode->RefreshRate);
					break;
				}
				}
				_windowState = value;
			}
		}

		public unsafe WindowBorder WindowBorder
		{
			get
			{
				return _windowBorder;
			}
			set
			{
				GLFW.GetVersion(out var major, out var minor, out var _);
				if (major == 3 && minor < 3)
				{
					throw new NotSupportedException("Cannot be implemented in GLFW 3.2.");
				}
				switch (value)
				{
				case WindowBorder.Hidden:
					GLFW.SetWindowAttrib(WindowPtr, WindowAttribute.Decorated, value: false);
					break;
				case WindowBorder.Resizable:
					GLFW.SetWindowAttrib(WindowPtr, WindowAttribute.Decorated, value: true);
					GLFW.SetWindowAttrib(WindowPtr, WindowAttribute.Resizable, value: true);
					break;
				case WindowBorder.Fixed:
					GLFW.SetWindowAttrib(WindowPtr, WindowAttribute.Decorated, value: true);
					GLFW.SetWindowAttrib(WindowPtr, WindowAttribute.Resizable, value: false);
					break;
				}
				_windowBorder = value;
			}
		}

		public unsafe Box2i Bounds
		{
			get
			{
				return new Box2i(Location, Location + Size);
			}
			set
			{
				GLFW.SetWindowSize(WindowPtr, value.Size.X, value.Size.Y);
				GLFW.SetWindowPos(WindowPtr, value.Min.X, value.Min.Y);
			}
		}

		public unsafe Vector2i Location
		{
			get
			{
				return _location;
			}
			set
			{
				GLFW.SetWindowPos(WindowPtr, value.X, value.Y);
				_location = value;
			}
		}

		public unsafe Vector2i Size
		{
			get
			{
				return _size;
			}
			set
			{
				_size = value;
				GLFW.SetWindowSize(WindowPtr, value.X, value.Y);
			}
		}

		public unsafe Vector2i? MinimumSize
		{
			get
			{
				return _minimumSize;
			}
			set
			{
				_minimumSize = value;
				GLFW.SetWindowSizeLimits(WindowPtr, value?.X ?? (-1), value?.Y ?? (-1), _maximumSize?.X ?? (-1), _maximumSize?.Y ?? (-1));
			}
		}

		public unsafe Vector2i? MaximumSize
		{
			get
			{
				return _maximumSize;
			}
			set
			{
				_maximumSize = value;
				GLFW.SetWindowSizeLimits(WindowPtr, _minimumSize?.X ?? (-1), _minimumSize?.Y ?? (-1), value?.X ?? (-1), value?.Y ?? (-1));
			}
		}

		public unsafe (int numerator, int denominator)? AspectRatio
		{
			get
			{
				return _aspectRatio;
			}
			set
			{
				_aspectRatio = value;
				GLFW.SetWindowAspectRatio(WindowPtr, value?.numerator ?? (-1), value?.denominator ?? (-1));
			}
		}

		public Box2i ClientRectangle
		{
			get
			{
				return new Box2i(Location, Location + Size);
			}
			set
			{
				Location = value.Min;
				Size = value.Size;
			}
		}

		public Vector2i ClientSize { get; private set; }

		public bool IsFullscreen => WindowState == WindowState.Fullscreen;

		public unsafe MouseCursor Cursor
		{
			get
			{
				return _managedCursor;
			}
			set
			{
				_managedCursor = value ?? throw new ArgumentNullException("value", "Cursor cannot be null. To reset to default cursor, set it to MouseCursor.Default instead.");
				Cursor* glfwCursor = _glfwCursor;
				_glfwCursor = null;
				if (value.Shape == MouseCursor.StandardShape.CustomShape)
				{
					fixed (byte* pixels = value.Data)
					{
						OpenTK.Windowing.GraphicsLibraryFramework.Image image = new OpenTK.Windowing.GraphicsLibraryFramework.Image(value.Width, value.Height, pixels);
						_glfwCursor = GLFW.CreateCursor(in image, value.X, value.Y);
					}
				}
				else if (value != MouseCursor.Default)
				{
					_glfwCursor = GLFW.CreateStandardCursor(MapStandardCursorShape(value.Shape));
				}
				GLFW.SetCursor(WindowPtr, _glfwCursor);
				if (glfwCursor != null)
				{
					GLFW.DestroyCursor(glfwCursor);
				}
			}
		}

		public unsafe CursorState CursorState
		{
			get
			{
				return GLFW.GetInputMode(WindowPtr, CursorStateAttribute.Cursor) switch
				{
					CursorModeValue.CursorNormal => CursorState.Normal, 
					CursorModeValue.CursorHidden => CursorState.Hidden, 
					CursorModeValue.CursorDisabled => CursorState.Grabbed, 
					_ => throw new ArgumentOutOfRangeException(), 
				};
			}
			set
			{
				GLFW.SetInputMode(value: value switch
				{
					CursorState.Normal => CursorModeValue.CursorNormal, 
					CursorState.Hidden => CursorModeValue.CursorHidden, 
					CursorState.Grabbed => CursorModeValue.CursorDisabled, 
					_ => throw new ArgumentOutOfRangeException(), 
				}, window: WindowPtr, mode: CursorStateAttribute.Cursor);
			}
		}

		[Obsolete("Use CursorState insatead.")]
		public unsafe bool CursorVisible
		{
			get
			{
				CursorModeValue inputMode = GLFW.GetInputMode(WindowPtr, CursorStateAttribute.Cursor);
				if (inputMode != CursorModeValue.CursorHidden)
				{
					return inputMode != CursorModeValue.CursorDisabled;
				}
				return false;
			}
			set
			{
				GLFW.SetInputMode(WindowPtr, CursorStateAttribute.Cursor, value ? CursorModeValue.CursorNormal : CursorModeValue.CursorHidden);
			}
		}

		[Obsolete("Use CursorState instead.")]
		public unsafe bool CursorGrabbed
		{
			get
			{
				return GLFW.GetInputMode(WindowPtr, CursorStateAttribute.Cursor) == CursorModeValue.CursorDisabled;
			}
			set
			{
				if (value)
				{
					GLFW.SetInputMode(WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorDisabled);
				}
				else if (CursorVisible)
				{
					GLFW.SetInputMode(WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorNormal);
				}
				else
				{
					GLFW.SetInputMode(WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorHidden);
				}
			}
		}

		public event Action<WindowPositionEventArgs> Move;

		public event Action<ResizeEventArgs> Resize;

		public event Action Refresh;

		public event Action<CancelEventArgs> Closing;

		[Obsolete("This event will never be invoked.")]
		public event Action Closed;

		public event Action<MinimizedEventArgs> Minimized;

		public event Action<MaximizedEventArgs> Maximized;

		public event Action<JoystickEventArgs> JoystickConnected;

		public event Action<FocusedChangedEventArgs> FocusedChanged;

		public event Action<KeyboardKeyEventArgs> KeyDown;

		public event Action<TextInputEventArgs> TextInput;

		public event Action<KeyboardKeyEventArgs> KeyUp;

		[Obsolete("Use the Monitors.OnMonitorConnected event instead.", true)]
		public event Action<MonitorEventArgs> MonitorConnected;

		public event Action MouseLeave;

		public event Action MouseEnter;

		public event Action<MouseButtonEventArgs> MouseDown;

		public event Action<MouseButtonEventArgs> MouseUp;

		public event Action<MouseMoveEventArgs> MouseMove;

		public event Action<MouseWheelEventArgs> MouseWheel;

		public event Action<FileDropEventArgs> FileDrop;

		public unsafe void Focus()
		{
			GLFW.FocusWindow(WindowPtr);
		}

		public unsafe NativeWindow(NativeWindowSettings settings)
		{
			GLFWProvider.EnsureInitialized();
			_title = settings.Title;
			_currentMonitor = settings.CurrentMonitor;
			switch (settings.WindowBorder)
			{
			case WindowBorder.Hidden:
				GLFW.WindowHint(WindowHintBool.Decorated, value: false);
				break;
			case WindowBorder.Resizable:
				GLFW.WindowHint(WindowHintBool.Resizable, value: true);
				break;
			case WindowBorder.Fixed:
				GLFW.WindowHint(WindowHintBool.Resizable, value: false);
				break;
			}
			bool flag = false;
			API = settings.API;
			switch (settings.API)
			{
			case ContextAPI.NoAPI:
				GLFW.WindowHint(WindowHintClientApi.ClientApi, ClientApi.NoApi);
				break;
			case ContextAPI.OpenGLES:
				GLFW.WindowHint(WindowHintClientApi.ClientApi, ClientApi.OpenGlEsApi);
				flag = true;
				break;
			case ContextAPI.OpenGL:
				GLFW.WindowHint(WindowHintClientApi.ClientApi, ClientApi.OpenGlApi);
				flag = true;
				break;
			default:
				throw new ArgumentOutOfRangeException();
			}
			GLFW.WindowHint(WindowHintInt.ContextVersionMajor, settings.APIVersion.Major);
			GLFW.WindowHint(WindowHintInt.ContextVersionMinor, settings.APIVersion.Minor);
			APIVersion = settings.APIVersion;
			Flags = settings.Flags;
			if (settings.Flags.HasFlag(ContextFlags.ForwardCompatible))
			{
				GLFW.WindowHint(WindowHintBool.OpenGLForwardCompat, value: true);
			}
			if (settings.Flags.HasFlag(ContextFlags.Debug))
			{
				GLFW.WindowHint(WindowHintBool.OpenGLDebugContext, value: true);
			}
			Profile = settings.Profile;
			switch (settings.Profile)
			{
			case ContextProfile.Any:
				GLFW.WindowHint(WindowHintOpenGlProfile.OpenGlProfile, OpenGlProfile.Any);
				break;
			case ContextProfile.Compatability:
				GLFW.WindowHint(WindowHintOpenGlProfile.OpenGlProfile, OpenGlProfile.Compat);
				break;
			case ContextProfile.Core:
				GLFW.WindowHint(WindowHintOpenGlProfile.OpenGlProfile, OpenGlProfile.Core);
				break;
			default:
				throw new ArgumentOutOfRangeException();
			}
			GLFW.WindowHint(WindowHintBool.Focused, settings.StartFocused);
			_windowBorder = settings.WindowBorder;
			_isVisible = settings.StartVisible;
			GLFW.WindowHint(WindowHintBool.Visible, _isVisible);
			GLFW.WindowHint(WindowHintInt.Samples, settings.NumberOfSamples);
			GLFW.WindowHint(WindowHintBool.SrgbCapable, settings.SrgbCapable);
			bool? transparentFramebuffer = settings.TransparentFramebuffer;
			if (transparentFramebuffer.HasValue)
			{
				bool valueOrDefault = transparentFramebuffer.GetValueOrDefault();
				GLFW.WindowHint(WindowHintBool.TransparentFramebuffer, valueOrDefault);
			}
			Monitor* monitor = settings.CurrentMonitor.ToUnsafePtr<Monitor>();
			VideoMode* videoMode = GLFW.GetVideoMode(monitor);
			GLFW.WindowHint(WindowHintInt.RedBits, settings.RedBits ?? videoMode->RedBits);
			GLFW.WindowHint(WindowHintInt.GreenBits, settings.GreenBits ?? videoMode->GreenBits);
			GLFW.WindowHint(WindowHintInt.BlueBits, settings.BlueBits ?? videoMode->BlueBits);
			if (settings.AlphaBits.HasValue)
			{
				GLFW.WindowHint(WindowHintInt.AlphaBits, settings.AlphaBits.Value);
			}
			if (settings.DepthBits.HasValue)
			{
				GLFW.WindowHint(WindowHintInt.DepthBits, settings.DepthBits.Value);
			}
			if (settings.StencilBits.HasValue)
			{
				GLFW.WindowHint(WindowHintInt.StencilBits, settings.StencilBits.Value);
			}
			GLFW.WindowHint(WindowHintInt.RefreshRate, videoMode->RefreshRate);
			if (settings.WindowState == WindowState.Fullscreen && _isVisible)
			{
				_windowState = WindowState.Fullscreen;
				_cachedWindowLocation = settings.Location ?? new Vector2i(32, 32);
				_cachedWindowClientSize = settings.Size;
				WindowPtr = GLFW.CreateWindow(videoMode->Width, videoMode->Height, _title, monitor, (Window*)(void*)(settings.SharedContext?.WindowPtr ?? IntPtr.Zero));
			}
			else
			{
				WindowPtr = GLFW.CreateWindow(settings.Size.X, settings.Size.Y, _title, null, (Window*)(void*)(settings.SharedContext?.WindowPtr ?? IntPtr.Zero));
			}
			if (settings.API != 0)
			{
				Context = new GLFWGraphicsContext(WindowPtr);
			}
			Exists = true;
			if (flag)
			{
				Context?.MakeCurrent();
				if (settings.AutoLoadBindings)
				{
					InitializeGlBindings();
				}
			}
			GLFW.SetInputMode(WindowPtr, LockKeyModAttribute.LockKeyMods, value: true);
			RegisterWindowCallbacks();
			InitialiseJoystickStates();
			_isFocused = settings.StartFocused;
			if (settings.StartFocused)
			{
				Focus();
			}
			if (settings.StartVisible)
			{
				WindowState = settings.WindowState;
			}
			IsEventDriven = settings.IsEventDriven;
			if (settings.Icon != null)
			{
				Icon = settings.Icon;
			}
			if (settings.Location.HasValue)
			{
				Location = settings.Location.Value;
			}
			GLFW.GetWindowSize(WindowPtr, out var width, out var height);
			HandleResize(width, height);
			AspectRatio = settings.AspectRatio;
			_minimumSize = settings.MinimumSize;
			_maximumSize = settings.MaximumSize;
			GLFW.SetWindowSizeLimits(WindowPtr, _minimumSize?.X ?? (-1), _minimumSize?.Y ?? (-1), _maximumSize?.X ?? (-1), _maximumSize?.Y ?? (-1));
			GLFW.GetWindowPos(WindowPtr, out var x, out var y);
			_location = new Vector2i(x, y);
			GLFW.GetCursorPos(WindowPtr, out var xPos, out var yPos);
			_lastReportedMousePos = new Vector2((float)xPos, (float)yPos);
			MouseState.Position = _lastReportedMousePos;
			_isFocused = GLFW.GetWindowAttrib(WindowPtr, WindowAttributeGetBool.Focused);
			if (API != 0)
			{
				VSync = settings.Vsync;
			}
		}
        static Assembly assembly;
        static void LoadBindings(string typeNamespace)
        {
            Type type = assembly.GetType("OpenTK.Graphics." + typeNamespace + ".GL");
            if (!(type == null))
            {
                type.GetMethod("LoadBindings")!.Invoke(null, new object[1] { provider });
            }
        }
        static GLFWBindingsContext provider;
        private static void InitializeGlBindings()
		{
			
			
			try
			{
				assembly = Assembly.Load("OpenTK.Graphics");
				
			}
			catch
			{
				return;
			}
			provider = new GLFWBindingsContext();
			LoadBindings("ES11");
			LoadBindings("ES20");
			LoadBindings("ES30");
			LoadBindings("OpenGL");
			LoadBindings("OpenGL4");
		}

		private unsafe void HandleResize(int width, int height)
		{
			_size.X = width;
			_size.Y = height;
			GLFW.GetFramebufferSize(WindowPtr, out width, out height);
			ClientSize = new Vector2i(width, height);
		}

		private unsafe WindowState GetWindowStateFromGLFW()
		{
			if (GLFW.GetWindowAttrib(WindowPtr, WindowAttributeGetBool.Iconified))
			{
				return WindowState.Minimized;
			}
			if (GLFW.GetWindowAttrib(WindowPtr, WindowAttributeGetBool.Maximized))
			{
				return WindowState.Maximized;
			}
			if (GLFW.GetWindowMonitor(WindowPtr) != null)
			{
				return WindowState.Fullscreen;
			}
			return WindowState.Normal;
		}

		private unsafe void RegisterWindowCallbacks()
		{
			_windowPosCallback = WindowPosCallback;
			_windowSizeCallback = WindowSizeCallback;
			_windowCloseCallback = WindowCloseCallback;
			_windowRefreshCallback = WindowRefreshCallback;
			_windowFocusCallback = WindowFocusCallback;
			_windowIconifyCallback = WindowIconifyCallback;
			_windowMaximizeCallback = WindowMaximizeCallback;
			_mouseButtonCallback = MouseButtonCallback;
			_cursorPosCallback = CursorPosCallback;
			_cursorEnterCallback = CursorEnterCallback;
			_scrollCallback = ScrollCallback;
			_keyCallback = KeyCallback;
			_charCallback = CharCallback;
			_dropCallback = DropCallback;
			_joystickCallback = JoystickCallback;
			GLFW.SetWindowPosCallback(WindowPtr, _windowPosCallback);
			GLFW.SetWindowSizeCallback(WindowPtr, _windowSizeCallback);
			GLFW.SetWindowCloseCallback(WindowPtr, _windowCloseCallback);
			GLFW.SetWindowRefreshCallback(WindowPtr, _windowRefreshCallback);
			GLFW.SetWindowFocusCallback(WindowPtr, _windowFocusCallback);
			GLFW.SetWindowIconifyCallback(WindowPtr, _windowIconifyCallback);
			GLFW.SetWindowMaximizeCallback(WindowPtr, _windowMaximizeCallback);
			GLFW.SetMouseButtonCallback(WindowPtr, _mouseButtonCallback);
			GLFW.SetCursorPosCallback(WindowPtr, _cursorPosCallback);
			GLFW.SetCursorEnterCallback(WindowPtr, _cursorEnterCallback);
			GLFW.SetScrollCallback(WindowPtr, _scrollCallback);
			GLFW.SetKeyCallback(WindowPtr, _keyCallback);
			GLFW.SetCharCallback(WindowPtr, _charCallback);
			GLFW.SetDropCallback(WindowPtr, _dropCallback);
			Joysticks.JoystickCallback += _joystickCallback;
		}

		private void UnregisterWindowCallbacks()
		{
			Joysticks.JoystickCallback -= _joystickCallback;
		}

		private unsafe void InitialiseJoystickStates()
		{
			for (int i = 0; i < _joystickStates.Length; i++)
			{
				if (GLFW.JoystickPresent(i))
				{
					GLFW.GetJoystickHatsRaw(i, out var count);
					GLFW.GetJoystickAxesRaw(i, out var count2);
					GLFW.GetJoystickButtonsRaw(i, out var count3);
					string joystickName = GLFW.GetJoystickName(i);
					_joystickStates[i] = new JoystickState(count, count2, count3, i, joystickName);
				}
			}
		}

		private unsafe void WindowPosCallback(Window* window, int x, int y)
		{
			try
			{
				OnMove(new WindowPositionEventArgs(x, y));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void WindowSizeCallback(Window* window, int width, int height)
		{
			try
			{
				OnResize(new ResizeEventArgs(width, height));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void WindowCloseCallback(Window* window)
		{
			try
			{
				CancelEventArgs cancelEventArgs = new CancelEventArgs();
				OnClosing(cancelEventArgs);
				if (cancelEventArgs.Cancel)
				{
					GLFW.SetWindowShouldClose(WindowPtr, value: false);
				}
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void WindowRefreshCallback(Window* window)
		{
			try
			{
				OnRefresh();
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void WindowFocusCallback(Window* window, bool focused)
		{
			try
			{
				OnFocusedChanged(new FocusedChangedEventArgs(focused));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void WindowIconifyCallback(Window* window, bool iconified)
		{
			try
			{
				OnMinimized(new MinimizedEventArgs(iconified));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void WindowMaximizeCallback(Window* window, bool maximized)
		{
			try
			{
				OnMaximized(new MaximizedEventArgs(maximized));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void MouseButtonCallback(Window* window, MouseButton button, InputAction action, KeyModifiers mods)
		{
			try
			{
				MouseButtonEventArgs e = new MouseButtonEventArgs(button, action, mods);
				if (action == InputAction.Release)
				{
					MouseState[button] = false;
					OnMouseUp(e);
				}
				else
				{
					MouseState[button] = true;
					OnMouseDown(e);
				}
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void CursorPosCallback(Window* window, double posX, double posY)
		{
			try
			{
				Vector2 vector = new Vector2((float)posX, (float)posY);
				Vector2 delta = vector - _lastReportedMousePos;
				_lastReportedMousePos = vector;
				OnMouseMove(new MouseMoveEventArgs(vector, delta));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void CursorEnterCallback(Window* window, bool entered)
		{
			try
			{
				if (entered)
				{
					OnMouseEnter();
				}
				else
				{
					OnMouseLeave();
				}
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void ScrollCallback(Window* window, double offsetX, double offsetY)
		{
			try
			{
				Vector2 vector = new Vector2((float)offsetX, (float)offsetY);
				MouseState.Scroll += vector;
				OnMouseWheel(new MouseWheelEventArgs(vector));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void KeyCallback(Window* window, Keys key, int scancode, InputAction action, KeyModifiers mods)
		{
			try
			{
				KeyboardKeyEventArgs e = new KeyboardKeyEventArgs(key, scancode, mods, action == InputAction.Repeat);
				if (action == InputAction.Release)
				{
					if (key != Keys.Unknown)
					{
						KeyboardState.SetKeyState(key, down: false);
					}
					OnKeyUp(e);
				}
				else
				{
					if (key != Keys.Unknown)
					{
						KeyboardState.SetKeyState(key, down: true);
					}
					OnKeyDown(e);
				}
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void CharCallback(Window* window, uint codepoint)
		{
			try
			{
				OnTextInput(new TextInputEventArgs((int)codepoint));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void DropCallback(Window* window, int count, byte** paths)
		{
			try
			{
				string[] array = new string[count];
				for (int i = 0; i < count; i++)
				{
					array[i] = MarshalUtility.PtrToStringUTF8(paths[i]);
				}
				OnFileDrop(new FileDropEventArgs(array));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		private unsafe void JoystickCallback(int joy, ConnectedState eventCode)
		{
			try
			{
				if (eventCode == ConnectedState.Connected)
				{
					GLFW.GetJoystickHatsRaw(joy, out var count);
					GLFW.GetJoystickAxesRaw(joy, out var count2);
					GLFW.GetJoystickButtonsRaw(joy, out var count3);
					string joystickName = GLFW.GetJoystickName(joy);
					_joystickStates[joy] = new JoystickState(count, count2, count3, joy, joystickName);
				}
				else
				{
					_joystickStates[joy] = null;
				}
				OnJoystickConnected(new JoystickEventArgs(joy, eventCode == ConnectedState.Connected));
			}
			catch (Exception source)
			{
				_callbackExceptions.Enqueue(ExceptionDispatchInfo.Capture(source));
			}
		}

		public unsafe virtual void Close()
		{
			CancelEventArgs cancelEventArgs = new CancelEventArgs();
			OnClosing(cancelEventArgs);
			if (!cancelEventArgs.Cancel)
			{
				GLFW.SetWindowShouldClose(WindowPtr, value: true);
			}
		}

		public void MakeCurrent()
		{
			if (Context == null)
			{
				throw new InvalidOperationException("Cannot make a context current when running with ContextAPI.NoAPI.");
			}
			Context?.MakeCurrent();
		}

		public bool ProcessEvents(double timeout)
		{
			GLFW.WaitEventsTimeout(timeout);
			ProcessInputEvents();
			RethrowCallbackExceptionsIfNeeded();
			return true;
		}

		[Obsolete("This function wrongly implies that only events from this window are processed, while in fact events for all windows are processed. Use NativeWindow.ProcessWindowEvents() instead.")]
		public virtual void ProcessEvents()
		{
			ProcessInputEvents();
			ProcessWindowEvents(IsEventDriven);
		}

		public static void ProcessWindowEvents(bool waitForEvents)
		{
			if (waitForEvents)
			{
				GLFW.WaitEvents();
			}
			else
			{
				GLFW.PollEvents();
			}
			RethrowCallbackExceptionsIfNeeded();
		}

		private static void RethrowCallbackExceptionsIfNeeded()
		{
			ExceptionDispatchInfo result;
			while (_callbackExceptions.TryDequeue(out result))
			{
				_localThreadExceptions.Add(result);
			}
			if (_localThreadExceptions.Count == 1)
			{
				ExceptionDispatchInfo exceptionDispatchInfo = _localThreadExceptions[0];
				_localThreadExceptions.Clear();
				exceptionDispatchInfo.Throw();
			}
			else if (_localThreadExceptions.Count > 1)
			{
				Exception[] array = new Exception[_localThreadExceptions.Count];
				for (int i = 0; i < _localThreadExceptions.Count; i++)
				{
					array[i] = _localThreadExceptions[i].SourceException;
				}
				AggregateException ex = new AggregateException("Multiple exceptions in callback handlers while processing events.", array);
				_localThreadExceptions.Clear();
				throw ex;
			}
		}

		public unsafe void ProcessInputEvents()
		{
			MouseState.Update();
			KeyboardState.Update();
			GLFW.GetCursorPos(WindowPtr, out var xPos, out var yPos);
			MouseState.Position = new Vector2((float)xPos, (float)yPos);
			for (int i = 0; i < _joystickStates.Length; i++)
			{
				if (_joystickStates[i] != null)
				{
					_joystickStates[i].Update();
				}
			}
		}

		public Vector2i PointToClient(Vector2i point)
		{
			return point - Location;
		}

		public Vector2i PointToScreen(Vector2i point)
		{
			return point + Location;
		}

		public bool IsKeyDown(Keys key)
		{
			return KeyboardState.IsKeyDown(key);
		}

		public bool IsKeyPressed(Keys key)
		{
			if (KeyboardState.IsKeyDown(key))
			{
				return !KeyboardState.WasKeyDown(key);
			}
			return false;
		}

		public bool IsKeyReleased(Keys key)
		{
			if (!KeyboardState.IsKeyDown(key))
			{
				return KeyboardState.WasKeyDown(key);
			}
			return false;
		}

		public bool IsMouseButtonDown(MouseButton button)
		{
			return MouseState.IsButtonDown(button);
		}

		public bool IsMouseButtonPressed(MouseButton button)
		{
			if (MouseState.IsButtonDown(button))
			{
				return !MouseState.WasButtonDown(button);
			}
			return false;
		}

		public bool IsMouseButtonReleased(MouseButton button)
		{
			if (!MouseState.IsButtonDown(button))
			{
				return MouseState.WasButtonDown(button);
			}
			return false;
		}

		[Obsolete("Use Monitors.GetMonitorFromWindow instead", true)]
		public unsafe MonitorInfo FindMonitor()
		{
			return Monitors.GetMonitorFromWindow(WindowPtr);
		}

		public bool TryGetCurrentMonitorScale(out float horizontalScale, out float verticalScale)
		{
			MonitorInfo monitorFromWindow = Monitors.GetMonitorFromWindow(this);
			horizontalScale = monitorFromWindow.HorizontalScale;
			verticalScale = monitorFromWindow.VerticalScale;
			return true;
		}

		public bool TryGetCurrentMonitorDpi(out float horizontalDpi, out float verticalDpi)
		{
			MonitorInfo monitorFromWindow = Monitors.GetMonitorFromWindow(this);
			horizontalDpi = monitorFromWindow.HorizontalDpi;
			verticalDpi = monitorFromWindow.VerticalDpi;
			return true;
		}

		public bool TryGetCurrentMonitorDpiRaw(out float horizontalRawDpi, out float verticalRawDpi)
		{
			MonitorInfo monitorFromWindow = Monitors.GetMonitorFromWindow(this);
			horizontalRawDpi = monitorFromWindow.HorizontalRawDpi;
			verticalRawDpi = monitorFromWindow.VerticalRawDpi;
			return true;
		}

		protected virtual void OnMove(WindowPositionEventArgs e)
		{
			this.Move?.Invoke(e);
			_location.X = e.X;
			_location.Y = e.Y;
		}

		protected virtual void OnResize(ResizeEventArgs e)
		{
			HandleResize(e.Width, e.Height);
			this.Resize?.Invoke(e);
		}

		protected virtual void OnRefresh()
		{
			this.Refresh?.Invoke();
		}

		protected virtual void OnClosing(CancelEventArgs e)
		{
			this.Closing?.Invoke(e);
		}

		[Obsolete("This method will never be called.")]
		protected virtual void OnClosed()
		{
			this.Closed?.Invoke();
		}

		protected virtual void OnJoystickConnected(JoystickEventArgs e)
		{
			this.JoystickConnected?.Invoke(e);
		}

		protected virtual void OnFocusedChanged(FocusedChangedEventArgs e)
		{
			this.FocusedChanged?.Invoke(e);
			_isFocused = e.IsFocused;
		}

		protected virtual void OnKeyDown(KeyboardKeyEventArgs e)
		{
			this.KeyDown?.Invoke(e);
		}

		protected virtual void OnTextInput(TextInputEventArgs e)
		{
			this.TextInput?.Invoke(e);
		}

		protected virtual void OnKeyUp(KeyboardKeyEventArgs e)
		{
			this.KeyUp?.Invoke(e);
		}

		[Obsolete("Use the Monitors.OnMonitorConnected event instead.", true)]
		protected virtual void OnMonitorConnected(MonitorEventArgs e)
		{
			this.MonitorConnected?.Invoke(e);
		}

		protected virtual void OnMouseLeave()
		{
			this.MouseLeave?.Invoke();
		}

		protected virtual void OnMouseEnter()
		{
			this.MouseEnter?.Invoke();
		}

		protected virtual void OnMouseDown(MouseButtonEventArgs e)
		{
			this.MouseDown?.Invoke(e);
		}

		protected virtual void OnMouseUp(MouseButtonEventArgs e)
		{
			this.MouseUp?.Invoke(e);
		}

		protected virtual void OnMouseMove(MouseMoveEventArgs e)
		{
			this.MouseMove?.Invoke(e);
		}

		protected virtual void OnMouseWheel(MouseWheelEventArgs e)
		{
			this.MouseWheel?.Invoke(e);
		}

		protected virtual void OnMinimized(MinimizedEventArgs e)
		{
			_windowState = (e.IsMinimized ? WindowState.Minimized : GetWindowStateFromGLFW());
			this.Minimized?.Invoke(e);
		}

		protected virtual void OnMaximized(MaximizedEventArgs e)
		{
			_windowState = (e.IsMaximized ? WindowState.Maximized : GetWindowStateFromGLFW());
			this.Maximized?.Invoke(e);
		}

		protected virtual void OnFileDrop(FileDropEventArgs e)
		{
			this.FileDrop?.Invoke(e);
		}

		protected unsafe virtual void Dispose(bool disposing)
		{
			if (!_disposedValue)
			{
				if (!GLFWProvider.IsOnMainThread)
				{
					throw new GLFWException("You can only dispose windows on the main thread. The window needs to be disposed as it cannot safely be disposed in the finalizer.");
				}
				UnregisterWindowCallbacks();
				GLFW.DestroyWindow(WindowPtr);
				Exists = false;
				_disposedValue = true;
			}
		}

		~NativeWindow()
		{
			Dispose(disposing: false);
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		private static CursorShape MapStandardCursorShape(MouseCursor.StandardShape shape)
		{
			return shape switch
			{
				MouseCursor.StandardShape.Arrow => CursorShape.Arrow, 
				MouseCursor.StandardShape.IBeam => CursorShape.IBeam, 
				MouseCursor.StandardShape.Crosshair => CursorShape.Crosshair, 
				MouseCursor.StandardShape.Hand => CursorShape.Hand, 
				MouseCursor.StandardShape.HResize => CursorShape.HResize, 
				MouseCursor.StandardShape.VResize => CursorShape.VResize, 
				_ => throw new ArgumentOutOfRangeException("shape", shape, null), 
			};
		}

		public void CenterWindow()
		{
			CenterWindow(Size);
		}

		public void CenterWindow(Vector2i newSize)
		{
			Box2i clientArea = Monitors.GetMonitorFromWindow(this).ClientArea;
			int num = (clientArea.Min.X + clientArea.Max.X - newSize.X) / 2;
			int num2 = (clientArea.Min.Y + clientArea.Max.Y - newSize.Y) / 2;
			if (num < clientArea.Min.X)
			{
				num = clientArea.Min.X;
			}
			if (num2 < clientArea.Min.Y)
			{
				num2 = clientArea.Min.Y;
			}
			ClientRectangle = new Box2i(num, num2, num + newSize.X, num2 + newSize.Y);
		}
	}
}
